package jerry.cn.polybasiccompress.PictureCacheALG;

import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class PictureCompress implements android.os.Handler.Callback {

    private static final String TAG = "PictureCompress";
    private static final String DEFAULT_DISK_CACHE_DIR = "PictureCompress_cache";

    private static final int MSG_START = 0;
    private static final int MSG_SUCCESS = 1;
    private static final int MSG_BAD = 2;

    private String TargetDir;
    private boolean FocusAlpha;
    private int NotCompressSize;
    private ALG_Result OnAlgResult;
    private OnRenameListener mRenameListener;
    private List<InputStreamResource> mInputStreamResource;

    private android.os.Handler mHandler;

    private PictureCompress(Construction construction) {
        this.FocusAlpha = construction.focusAlpha;
        this.NotCompressSize = construction.mLeastCompressSize;
        this.OnAlgResult = construction.OnAlgResult;
        this.mRenameListener = construction.mRenameListener;
        this.TargetDir = construction.mTargetDir;
        this.mInputStreamResource = construction.mInputStreamResource;
        mHandler = new android.os.Handler(Looper.getMainLooper(), this);
    }

    public static Construction with(Context context) {
        return new Construction(context);
    }

    private void StartPictureCompress(final Context context) {
        if (mInputStreamResource == null || mInputStreamResource.size() == 0 && OnAlgResult != null) {
            OnAlgResult.OnBad(new NullPointerException("pass by value cannot be null"));
        }

        Iterator<InputStreamResource> iterator = mInputStreamResource.iterator();

        while (iterator.hasNext()) {
            final InputStreamResource path = iterator.next();

            File Dir = new File(TargetDir);
            if (!Dir.exists()){
                Dir.mkdir();
                Log.i("Jerry","创建了");
            }

            AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        mHandler.sendMessage(mHandler.obtainMessage(MSG_START));

                        File result = compress(context, path);

                        mHandler.sendMessage(mHandler.obtainMessage(MSG_SUCCESS, result));
                    } catch (IOException e) {
                        mHandler.sendMessage(mHandler.obtainMessage(MSG_BAD, e));
                    }
                }
            });

            iterator.remove();
        }
    }

    private File get(InputStreamResource input, Context context) throws IOException {
        return new Engine(input, getImageCacheFile(context, CheckAll.SINGLE.extSuffix(input)), FocusAlpha).compress();
    }

    private List<File> get(Context context) throws IOException {
        List<File> results = new ArrayList<>();
        Iterator<InputStreamResource> iterator = mInputStreamResource.iterator();

        while (iterator.hasNext()) {
            results.add(compress(context, iterator.next()));
            iterator.remove();
        }
        return results;
    }

    private File getImageCacheFile(Context context, String suffix) {
        if (TextUtils.isEmpty(TargetDir)) {
            TargetDir = getImageCacheDir(context).getAbsolutePath();
        }

        String cacheBuilder = TargetDir + "/" +
                System.currentTimeMillis() +
                (int) (Math.random() * 1000) +
                (TextUtils.isEmpty(suffix) ? ".jpg" : suffix);

        return new File(cacheBuilder);
    }

    private File compress(Context context, InputStreamResource path) throws IOException {
        File result;

        File outFile = getImageCacheFile(context, CheckAll.SINGLE.extSuffix(path));

        if (mRenameListener != null) {
            String filename = mRenameListener.rename(path.getPath());
            outFile = getImageCustomFile(context, filename);
            Log.i(TAG, outFile.toString());
        }

        if (!(TextUtils.isEmpty(path.getPath()) || path.getPath().toLowerCase().endsWith(".gif"))
                && CheckAll.SINGLE.needCompress(NotCompressSize, path.getPath())) {
            result = new Engine(path, outFile, FocusAlpha).compress();
        } else {
            result = new File(path.getPath());
        }

        return result;
    }


    private File getImageCustomFile(Context context, String filename) {
        if (TextUtils.isEmpty(TargetDir)) {
            TargetDir = getImageCacheDir(context).getAbsolutePath();
        }

        String cacheBuilder = TargetDir + "/" + filename;

        return new File(cacheBuilder);
    }


    private File getImageCacheDir(Context context) {
        return getImageCacheDir(context, DEFAULT_DISK_CACHE_DIR);
    }

    private static File getImageCacheDir(Context context, String cacheName) {
        File cacheDir = context.getExternalCacheDir();
        if (cacheDir != null) {
            File result = new File(cacheDir, cacheName);
            if (!result.mkdirs() && (!result.exists() || !result.isDirectory())) {
                return null;
            }
            return result;
        }
        if (Log.isLoggable(TAG, Log.ERROR)) {
            Log.e(TAG, "default disk cache dir is null");
        }
        return null;
    }

    @Override
    public boolean handleMessage(Message msg) {
        if (OnAlgResult == null) return false;

        switch (msg.what) {
            case MSG_START:
                OnAlgResult.OnStart();
                break;
            case MSG_SUCCESS:
                OnAlgResult.OnSuccess((File) msg.obj);
                break;
            case MSG_BAD:
                OnAlgResult.OnBad((Exception) msg.obj);
                break;
        }
        return false;
    }

    public static class Construction {
        private Context context;
        private String mTargetDir;
        private boolean focusAlpha;
        private int mLeastCompressSize = 100;
        private ALG_Result OnAlgResult;
        private OnRenameListener mRenameListener;
        private List<InputStreamResource> mInputStreamResource;

        Construction(Context context) {
            this.context = context;
            this.mInputStreamResource = new ArrayList<>();
        }

        private PictureCompress build() {
            return new PictureCompress(this);
        }

        private PictureCompress construction() {
            return new PictureCompress(this);
        }

        public Construction WriteInNeedDir(InputStreamResource inputStreamResource) {
            mInputStreamResource.add(inputStreamResource);
            return this;
        }

        public Construction WriteInNeedDir(final File file) {
            mInputStreamResource.add(new InputStreamResource() {
                @Override
                public InputStream open() throws IOException {
                    return new FileInputStream(file);
                }

                @Override
                public String getPath() throws NullPointerException {
                    return file.getAbsolutePath();
                }
            });
            return this;
        }

        public Construction WriteInNeedDir(final String path) {
            mInputStreamResource.add(new InputStreamResource() {
                @Override
                public InputStream open() throws IOException {
                    return new FileInputStream(path);
                }

                @Override
                public String getPath() throws NullPointerException {
                    return path;
                }
            });
            return this;
        }

        public Construction WriteInNeedDir(final Uri uri) {
            mInputStreamResource.add(new InputStreamResource() {
                @Override
                public InputStream open() throws IOException {
                    return context.getContentResolver().openInputStream(uri);
                }

                @Override
                public String getPath() throws NullPointerException {
                    return uri.getPath();
                }
            });
            return this;
        }

        public <T> Construction WriteInNeedDir(List<T> list) {
            for (T src : list) {
                if (src instanceof String) {
                    WriteInNeedDir((String) src);
                } else if (src instanceof File) {
                    WriteInNeedDir((File) src);
                } else if (src instanceof Uri) {
                    WriteInNeedDir((Uri) src);
                } else {
                    throw new IllegalArgumentException("Resources that cannot be converted");
                }
            }
            return this;
        }

        public Construction set_alg_result(ALG_Result alg_result) {
            this.OnAlgResult = alg_result;
            return this;
        }

        public Construction set_targetDir(String AskPath) {
            this.mTargetDir = AskPath;
            return this;
        }

        public Construction set_targetDir(File AskFile) {
            this.mTargetDir = AskFile.toString();
            return this;
        }

        public Construction setMinCompressSize(int size) {
            this.mLeastCompressSize = size;
            return this;
        }

        public Construction setFocusAlpha(boolean focusAlpha) {
            this.focusAlpha = focusAlpha;
            return this;
        }

        public void StartConstruction() {
            build().StartPictureCompress(context);
        }

        public Construction setRenameListener(OnRenameListener listener) {
            this.mRenameListener = listener;
            return this;
        }

        public File get(final String path) throws IOException {
            return build().get(new InputStreamResource() {
                @Override
                public InputStream open() throws IOException {
                    return new FileInputStream(path);
                }

                @Override
                public String getPath() {
                    return path;
                }
            }, context);
        }

        public List<File> get() throws IOException {
            return build().get(context);
        }
    }
}
